import socket
import json
import threading
import time
import tkinter as tk
from tkinter import ttk, messagebox
import platform
import getpass
import uuid
import netifaces
import configparser
import os


class NetworkLabelClient:
    def __init__(self, root):
        self.root = root
        self.root.title("网络线路标号系统 - 客户端")
        self.root.geometry("600x500")
        self.root.resizable(False, False)

        # 客户端配置
        self.server_host = ""
        self.server_port = 12345
        self.client_socket = None
        self.connected = False
        self.selected_nic = None  # 选中的网卡

        # 从配置文件加载服务器信息
        self.load_server_config()

        # 创建UI
        self.create_widgets()

        # 尝试获取默认服务器地址（同网段）
        if not self.server_host:
            self.auto_detect_server()

    def create_widgets(self):
        # 服务器连接设置
        server_frame = ttk.LabelFrame(self.root, text="服务器设置")
        server_frame.pack(fill=tk.X, padx=10, pady=5)

        ttk.Label(server_frame, text="服务器IP地址:").grid(row=0, column=0, padx=5, pady=10, sticky=tk.W)
        self.server_ip_var = tk.StringVar(value=self.server_host)
        ttk.Entry(server_frame, textvariable=self.server_ip_var, width=20).grid(row=0, column=1, padx=5, pady=10)

        ttk.Label(server_frame, text="端口:").grid(row=0, column=2, padx=5, pady=10, sticky=tk.W)
        self.server_port_var = tk.StringVar(value=str(self.server_port))
        ttk.Entry(server_frame, textvariable=self.server_port_var, width=8).grid(row=0, column=3, padx=5, pady=10)

        self.connect_btn = ttk.Button(server_frame, text="连接服务器", command=self.toggle_connection)
        self.connect_btn.grid(row=0, column=4, padx=10, pady=10)

        # 设备信息
        info_frame = tk.LabelFrame(self.root, text="设备信息")
        info_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        # 房间号
        ttk.Label(info_frame, text="房间号:").grid(row=0, column=0, padx=5, pady=10, sticky=tk.W)
        self.room_var = tk.StringVar()
        ttk.Entry(info_frame, textvariable=self.room_var, width=20).grid(row=0, column=1, padx=5, pady=10)

        # 电脑名称
        ttk.Label(info_frame, text="电脑名称:").grid(row=1, column=0, padx=5, pady=10, sticky=tk.W)
        self.computer_name_var = tk.StringVar(value=platform.node())
        ttk.Entry(info_frame, textvariable=self.computer_name_var, width=20).grid(row=1, column=1, padx=5, pady=10)

        # 网卡信息列表 - 可选择
        ttk.Label(info_frame, text="物理网卡 (请选择):").grid(row=2, column=0, padx=5, pady=5, sticky=tk.NW)

        # 创建网卡信息表格
        columns = ("interface", "ip", "mac")
        self.nic_tree = ttk.Treeview(info_frame, columns=columns, show="headings", height=5)

        # 设置列标题
        self.nic_tree.heading("interface", text="网卡接口")
        self.nic_tree.heading("ip", text="IP地址")
        self.nic_tree.heading("mac", text="MAC地址")

        # 设置列宽
        self.nic_tree.column("interface", width=100)
        self.nic_tree.column("ip", width=130)
        self.nic_tree.column("mac", width=170)

        # 绑定选择事件
        self.nic_tree.bind("<<TreeviewSelect>>", self.on_nic_select)

        self.nic_tree.grid(row=2, column=1, padx=5, pady=5, sticky=tk.NSEW)

        # 添加滚动条
        scrollbar = ttk.Scrollbar(info_frame, orient=tk.VERTICAL, command=self.nic_tree.yview)
        self.nic_tree.configure(yscroll=scrollbar.set)
        scrollbar.grid(row=2, column=2, sticky=tk.NS)

        # 状态区域
        status_frame = tk.LabelFrame(self.root, text="连接状态")
        status_frame.pack(fill=tk.X, padx=10, pady=5)

        self.status_var = tk.StringVar(value="未连接")
        self.status_label = ttk.Label(status_frame, textvariable=self.status_var, foreground="red")
        self.status_label.pack(padx=5, pady=5, anchor=tk.W)

        # 底部按钮
        btn_frame = ttk.Frame(self.root)
        btn_frame.pack(fill=tk.X, padx=10, pady=10)

        ttk.Button(btn_frame, text="刷新本机信息", command=self.refresh_local_info).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="发送信息", command=self.send_info).pack(side=tk.RIGHT, padx=5)

        # 初始加载网卡信息
        self.refresh_local_info()

    def on_nic_select(self, event):
        """处理网卡选择事件"""
        selected_items = self.nic_tree.selection()
        if selected_items:
            # 获取选中项的值
            item = selected_items[0]
            values = self.nic_tree.item(item, "values")
            self.selected_nic = {
                'interface': values[0],
                'ip_address': values[1],
                'mac_address': values[2]
            }
            # 更新状态显示
            if self.connected:
                self.status_var.set(
                    f"已连接到 {self.server_host}:{self.server_port}\n使用网卡: {self.selected_nic['ip_address']} ({self.selected_nic['mac_address']})")

    def is_virtual_interface(self, iface):
        """判断是否为虚拟网卡"""
        # 常见虚拟网卡关键字列表
        virtual_keywords = [
            'virtual', 'vpn', 'vmware', 'virtualbox', 'hyper-v',
            'docker', 'tap', 'tun', 'loopback', 'wireshark',
            'sangfor', 'vmnet', 'vnic'
        ]

        # 转换为小写进行比较
        iface_lower = iface.lower()

        # 检查是否包含虚拟网卡关键字
        for keyword in virtual_keywords:
            if keyword in iface_lower:
                return True

        # 检查是否为回环接口
        if iface_lower.startswith('lo'):
            return True

        return False

    def get_physical_nics(self):
        """获取所有物理网卡信息，跳过虚拟网卡"""
        nics = []

        try:
            # 获取所有网络接口
            interfaces = netifaces.interfaces()
            for iface in interfaces:
                # 跳过虚拟网卡
                if self.is_virtual_interface(iface):
                    continue

                # 获取接口信息
                nic_info = {
                    'interface': iface,
                    'ip_address': '',
                    'mac_address': ''
                }

                # 获取MAC地址
                addrs = netifaces.ifaddresses(iface)
                if netifaces.AF_LINK in addrs:
                    mac_info = addrs[netifaces.AF_LINK][0]
                    nic_info['mac_address'] = mac_info.get('addr', '')

                # 获取IPv4地址
                if netifaces.AF_INET in addrs:
                    ip_info = addrs[netifaces.AF_INET][0]
                    nic_info['ip_address'] = ip_info.get('addr', '')

                # 只添加有MAC地址的有效网卡
                if nic_info['mac_address'] and nic_info['mac_address'] != '00:00:00:00:00:00':
                    nics.append(nic_info)
        except Exception as e:
            self.log(f"获取网卡信息时出错: {str(e)}")

        return nics

    def load_server_config(self):
        """从配置文件加载服务器信息"""
        config_file = "config.ini"
        if os.path.exists(config_file):
            config = configparser.ConfigParser()
            config.read(config_file)
            if 'Server' in config:
                self.server_host = config['Server'].get('IP', '')
                self.server_port = int(config['Server'].get('Port', str(self.server_port)))

    def auto_detect_server(self):
        """自动检测同网段的服务器"""
        # 获取本机IP地址前缀
        nics = self.get_physical_nics()
        if nics and nics[0]['ip_address'] and '.' in nics[0]['ip_address']:
            ip_prefix = '.'.join(nics[0]['ip_address'].split('.')[:-1]) + '.'
            self.server_ip_var.set(f"{ip_prefix}1")  # 假设服务器在同网段的.1地址

    def refresh_local_info(self):
        """刷新本机信息，特别是网卡信息"""
        # 清空现有网卡信息
        for item in self.nic_tree.get_children():
            self.nic_tree.delete(item)

        # 获取并显示物理网卡信息
        nics = self.get_physical_nics()
        for nic in nics:
            self.nic_tree.insert(
                "", tk.END,
                values=(
                    nic['interface'],
                    nic['ip_address'],
                    nic['mac_address']
                )
            )

        # 默认选择第一个网卡
        if nics:
            self.nic_tree.selection_set(self.nic_tree.get_children()[0])
            self.on_nic_select(None)

        # 更新电脑名称
        self.computer_name_var.set(platform.node())

    def toggle_connection(self):
        """切换连接状态（连接/断开）"""
        if self.connected:
            self.disconnect()
        else:
            self.connect()

    def connect(self):
        """连接到服务器"""
        try:
            # 检查是否选择了网卡
            if not self.selected_nic:
                messagebox.showwarning("警告", "请先选择一个物理网卡")
                return

            self.server_host = self.server_ip_var.get()
            self.server_port = int(self.server_port_var.get())

            # 检查房间号是否填写
            if not self.room_var.get().strip():
                messagebox.showwarning("警告", "请先填写房间号")
                return

            # 创建socket并连接
            self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.client_socket.connect((self.server_host, self.server_port))

            self.connected = True
            self.status_var.set(
                f"已连接到 {self.server_host}:{self.server_port}\n使用网卡: {self.selected_nic['ip_address']} ({self.selected_nic['mac_address']})")
            self.status_label.config(foreground="green")
            self.connect_btn.config(text="断开连接")

            # 发送设备信息
            self.send_info()

            # 启动接收线程
            self.receive_thread = threading.Thread(target=self.receive_data, daemon=True)
            self.receive_thread.start()

        except Exception as e:
            messagebox.showerror("连接失败", f"无法连接到服务器: {str(e)}")
            self.connected = False
            self.status_var.set("连接失败")
            self.status_label.config(foreground="red")

    def disconnect(self):
        """断开与服务器的连接"""
        if self.client_socket:
            try:
                self.client_socket.close()
            except:
                pass

        self.connected = False
        self.status_var.set("已断开连接")
        self.status_label.config(foreground="red")
        self.connect_btn.config(text="连接服务器")

    def send_info(self):
        """发送选中的网卡信息到服务器"""
        if not self.connected or not self.client_socket:
            messagebox.showwarning("警告", "请先连接到服务器")
            return

        # 检查是否选择了网卡
        if not self.selected_nic:
            messagebox.showwarning("警告", "请先选择一个物理网卡")
            return

        try:
            # 获取所有物理网卡信息
            all_nics = self.get_physical_nics()

            # 收集设备信息，使用选中的网卡作为主要标识
            device_info = {
                "room_number": self.room_var.get().strip(),
                "computer_name": self.computer_name_var.get().strip(),
                "mac_address": self.selected_nic['mac_address'],  # 选中的MAC地址
                "ip_address": self.selected_nic['ip_address'],  # 选中的IP地址
                "username": getpass.getuser(),  # 当前登录用户名
                "all_nics": all_nics  # 所有物理网卡信息
            }

            # 发送信息
            data = json.dumps(device_info).encode('utf-8')
            self.client_socket.sendall(data)
            messagebox.showinfo("成功", f"设备信息已发送\n使用网卡: {self.selected_nic['ip_address']}")

        except Exception as e:
            messagebox.showerror("发送失败", f"发送信息时出错: {str(e)}")
            self.disconnect()

    def receive_data(self):
        """接收服务器数据"""
        while self.connected and self.client_socket:
            try:
                data = self.client_socket.recv(1024)
                if not data:
                    # 服务器关闭连接
                    self.root.after(0, self.show_disconnect_message)
                    break

                # 处理服务器发送的信息
                message = data.decode('utf-8')
                if message == 'ping':
                    # 响应心跳包
                    self.client_socket.sendall(b'pong')
                else:
                    self.root.after(0, lambda m=message: messagebox.showinfo("服务器消息", m))

            except Exception as e:
                # 修复e变量作用域问题
                error_msg = f"通信错误: {str(e)}"
                self.root.after(0, lambda m=error_msg: self.show_error_message(m))
                break

        # 如果循环结束且仍处于连接状态，则断开连接
        if self.connected:
            self.root.after(0, self.disconnect)

    def show_disconnect_message(self):
        """显示断开连接消息"""
        messagebox.showwarning("连接断开", "与服务器的连接已断开")
        self.disconnect()

    def show_error_message(self, message):
        """显示错误消息"""
        messagebox.showerror("错误", message)
        self.disconnect()


if __name__ == "__main__":
    root = tk.Tk()
    app = NetworkLabelClient(root)
    root.mainloop()
